package hollycrm.com.libuip.internal;

import android.support.annotation.RestrictTo;
import android.util.Log;

import org.json.JSONObject;

import java.net.URISyntaxException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import hollycrm.com.libuip.socket.SocketClient;
import io.socket.client.IO;
import io.socket.client.Socket;
import io.socket.emitter.Emitter;

/**
 * Created by aber on 2017/1/17.
 * {@hide}
 */
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class SocketIOClient implements SocketClient {

    private static final boolean DEBUG = true;

    private Socket socket;
    private Callback callback;
    private String host;
    private int port;
    private String token;

    private ScheduledThreadPoolExecutor threadPoolExecutor
            = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);

    private String heart;
    private long lastTime;

    private Runnable heartRunnable = new Runnable() {
        @Override
        public void run() {
            long current = System.currentTimeMillis();
            if (current - lastTime > 60 * 1000) {
                threadPoolExecutor.remove(heartRunnable);
                socket.disconnect();
            }else {
                sendHeart(heart);
            }
        }
    };

    public SocketIOClient(String host,String token,int port, SocketClient.Callback callback) {
        this.host = host;
        this.port = port;
        this.token = token;
        this.callback = callback;
    }

    public void setHeartMessage(String message) {
        this.heart = message;
    }

    @Override
    public void send(String message) {
        if (callback != null)
            callback.onSend(message);
        socket.emit("Data",message + "$");
    }

    @Override
    public void sendHeart(String message) {
        if (callback != null)
            callback.onSend(message);
        socket.emit("Heartbeat",message + "$");
    }

    @Override
    public void setCallback(Callback callback) {
        this.callback = callback;
    }

    @Override
    public void connect() {
        try {
            IO.Options options = new IO.Options();
            // the is a bug if reconnected set true
            // socketIO client bug
            // 关闭网络 ，然后打开网络 ， socket 链路是通的可以发送消息，
            // 但是 response 消息 无法接收到
            options.reconnection = true;
            options.query = "token="+token;
            socket = IO.socket("http://"+host+":"+port,options);
            connect(socket);
            //threadPoolExecutor.scheduleAtFixedRate(heartRunnable,6,6,TimeUnit.SECONDS);
        } catch (URISyntaxException e) {
            e.printStackTrace();
            if (callback != null)
                callback.onError(e);
        }
    }

    @Override
    public void disconnect() {
        if (socket != null) {
            socket.disconnect();
            socket.off();
            threadPoolExecutor.remove(heartRunnable);
            threadPoolExecutor.shutdown();
        }
    }

    private void connect(Socket socket) {
        socket.on(Socket.EVENT_CONNECT, onConnect)
                .on(Socket.EVENT_CONNECTING, onConnecting)
                .on(Socket.EVENT_DISCONNECT, onDisConnected)
                .on(Socket.EVENT_ERROR, onError)
                .on(Socket.EVENT_CONNECT_ERROR, onError)
                .on(Socket.EVENT_CONNECT_TIMEOUT, onError)
                .on(Socket.EVENT_RECONNECT,onReconnected)
                .on(Socket.EVENT_RECONNECT_ERROR,onREconnectedError)
                .on("Heartbeat",onHeart)
                .on("Data", onReceived);
        socket.connect();
    }

    @Override
    public boolean isRunning() {
        return socket != null && socket.connected();
    }

    private Emitter.Listener onConnect = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            Log.e("connected---",SocketIOClient.this.toString());
            lastTime = System.currentTimeMillis();
            if (callback != null)
                callback.onConnected();
        }
    };

    private Emitter.Listener onConnecting = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            if (callback != null) {
                callback.onConnecting();
            }
        }
    };

    private Emitter.Listener onDisConnected = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            if (callback != null) {
                callback.onDisconnected();
            }
        }
    };

    private Emitter.Listener onReceived = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            lastTime = System.currentTimeMillis();
            if (callback != null) {
                try {
                    String json = (String) args[0];
                    callback.onReceived(json.substring(0,json.length() - 1));
                }catch (Exception e) {
                    callback.onError(e);
                }
            }
        }
    };

    private Emitter.Listener onError = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            if (callback != null) {
                if (args[0] instanceof Throwable) {
                    Log.e("----SocketIOError----","error", (Throwable) args[0]);
                }
            }
        }
    };

    private Emitter.Listener onHeart = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            lastTime = System.currentTimeMillis();
            if (callback != null) {
                String json = (String) args[0];
                callback.onReceived(json.substring(0,json.length() - 1));
            }
        }
    };

    private Emitter.Listener onReconnected = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            Log.e("reconnected","ok");
            disconnect();
            connect();
        }
    };

    private Emitter.Listener onREconnectedError = new Emitter.Listener() {
        @Override
        public void call(Object... args) {
            Log.e("reconnectedError","ok");
        }
    };
}
